package com.zhang.common.jpa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
import com.zhang.common.jpa.entity.*;
import com.zhang.common.jpa.rep.BaseRep;
import com.zhang.common.jpa.service.BaseService;
import com.zhang.common.jpa.specification.EntitySpecification;
import com.zhang.constants.WebConstant;
import com.zhang.exception.exception.ParamLackException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class BaseServiceImpl<REP extends BaseRep<T,ID>,T extends BaseEntity,ID> implements BaseService<T,ID> {

    @Autowired
    private REP rep;

    public <R extends DTO> Page<R> page(T queryEntity, Map<String,String[]> queryMap){

        EntitySpecification<T> queryParam = new EntitySpecification<>(queryEntity, queryMap);

        //分页参数
        String[] pageNum = queryMap.get(WebConstant.PAGE_NUM);
        String[] pageSize = queryMap.get(WebConstant.PAGE_SIZE);
        if (pageNum == null && pageSize == null) throw new ParamLackException("缺少必要的分页参数");

        Page<T> page = rep.findAll(queryParam, PageRequest.of(Integer.parseInt(pageNum[0])-1,Integer.parseInt(pageSize[0])));
        return convert2Dto(page,this::convert2DtoSingle);
    }

    public <R extends DTO> Page<R> page(Supplier<T> supplier,PageCmd pageCmd,Map<String,String[]> queryMap){

        //转换Dto对象
        T queryEntity = supplier.get();
        BeanUtils.copyProperties(pageCmd,queryEntity);
        EntitySpecification<T> queryParam = new EntitySpecification<>(queryEntity, queryMap);

        //分页参数
        String[] pageNum = queryMap.get(WebConstant.PAGE_NUM);
        String[] pageSize = queryMap.get(WebConstant.PAGE_SIZE);
        if (pageNum == null && pageSize == null) throw new ParamLackException("缺少必要的分页参数");

        Page<T> page = rep.findAll(queryParam, PageRequest.of(Integer.parseInt(pageNum[0])-1,Integer.parseInt(pageSize[0])));
        return convert2Dto(page,this::convert2DtoSingle);
    }

    public  <R extends DTO> Page<R> convert2Dto(Page page,Function<? super T, ? extends R> Converter){
        List<R> collect = (List)page.getContent().stream().map(Converter).collect(Collectors.toList());
        return new PageImpl<>(collect,page.getPageable(),page.getTotalElements());
    }

    /**
     * 转换单个对象
     * 要使用列表查询的必须重写转换为DTO对象的方法
     */
    public <R extends DTO> R  convert2DtoSingle(T orginInfo){
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public T save(T entity) {
        if (entity == null) throw new RuntimeException("存储对象不能为空");
        return rep.save(initData(entity));
    }

    @Override
    public T save(AddCmd addCmd, Supplier<T> Constructor) {
        T entity = Constructor.get();
        BeanUtils.copyProperties(addCmd,entity);
        return save(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<T> saveAll(List<T> entities) {
        ArrayList<T> result = new ArrayList<>();
        //List为空，直接返回
        if (entities == null || entities.size() < 1) return result;
        entities.forEach(item ->result.add(save(item)));
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean update(T entity) {
        if (entity == null) throw new RuntimeException("存储对象不能为空");
        if (entity.getId() ==null || entity.getId() == "") throw new RuntimeException("存储对象ID不能为空");
        //TODO 添加修改人
        entity.setUpdateTime(new Date());

        //保存对象
        rep.save(entity);
        return true;
    }

    /**
     * jpa的更新机制 必须先查出来 然后会调用merge方法
     */
    @Override
    public boolean update(EditCmd editCmd) {
        T entity = get((ID) editCmd.getId());
        BeanUtil.copyProperties(editCmd,entity, CopyOptions.create().setIgnoreNullValue(true));
        return update(entity);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateAll(List<T> entities) {
        if (entities == null || entities.size() < 1) return true;
        entities.forEach(item ->update(item));
        return true;
    }

    @Override
    public T get(ID id) {
        return rep.findById(id).orElse(null);
    }

    @Override
    public List<T> getByIds(Set<ID> ids) {
        return rep.findAllById(ids);
    }

    @Override
    public List<T> getAll() {
        return rep.findAll();
    }

    @Override
    public T getLogic(ID id) {
        return rep.findByIdAndDeleted(id,false).orElse(null);
    }

    @Override
    public Set<T> getByIdsLogic(Set<ID> ids) {
        return rep.findByIdInAndDeleted(ids,false);
    }

    @Override
    public Set<T> getByIdsLogic(String ids) {
        ID[] split = (ID[]) ids.split(",");
        Set<ID> sets = new HashSet<>(Arrays.asList(split));
        return getByIdsLogic(sets);
    }

    @Override
    public List<T> getAllLogic() {
        return rep.findByDeleted(false);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean delete(ID id) {
        T entity = getEntity(id);
        if (entity == null) return true;

        rep.deleteById(id);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteAll(Set<ID> ids) {
        if (ids != null && ids.size() > 1){
            ids.forEach(id ->delete(id));
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteLogic(ID id) {
        T entity = getEntity(id);
        if (entity == null || entity.getDeleted()) return true;

        entity.setDeleted(Boolean.TRUE);
        entity.setUpdateTime(new Date());
        //TODO 设置修改人

        rep.save(entity);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteAllLogic(Set<ID> ids) {
        if (ids != null && ids.size() > 1){
            ids.forEach(id ->deleteLogic(id));
        }
        return true;
    }


    /**
     * 初始化数据
     * @param entity
     * @return
     */
    private T initData(T entity){
        if (!StringUtils.hasLength(entity.getId())){
            entity.setId(String.valueOf(UUID.randomUUID()));
        }
        entity.setDeleted(false);
        //设置时间
        Date now = new Date();
        entity.setCreateTime(now);
        entity.setUpdateTime(now);
        //TODO 设置创建人和更新人

        return entity;
    }


    /**
     * 获取对象
     * @param id
     * @return
     */
    private T getEntity(ID id){
        if (id == null){
            return null;
        }
        return rep.findById(id).orElse(null);
    }


    /**
     * @return Repository对象
     */
    public REP getRep() {
        if (rep == null || !(rep instanceof BaseRep)){
            throw new RuntimeException("rep 类型为null或者类型不匹配");
        }
        return (REP) rep;
    }
}
